/*
 *   The contents of this file are subject to the Mozilla Public License
 *   Version 1.1 (the "License"); you may not use this file except in
 *   compliance with the License. You may obtain a copy of the License at
 *   http://www.mozilla.org/MPL/
 *
 *   Software distributed under the License is distributed on an "AS IS"
 *   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 *   License for the specific language governing rights and limitations
 *   under the License.
 *
 *   The Original Code is Matra - the DTD Parser.
 *
 *   The Initial Developer of the Original Code is Conrad S Roche.
 *   Portions created by Conrad S Roche are Copyright (C) Conrad
 *   S Roche. All Rights Reserved.
 *
 *   Alternatively, the contents of this file may be used under the terms
 *   of the GNU GENERAL PUBLIC LICENSE Version 2 or any later version
 *   (the  "[GPL] License"), in which case the
 *   provisions of GPL License are applicable instead of those
 *   above.  If you wish to allow use of your version of this file only
 *   under the terms of the GPL License and not to allow others to use
 *   your version of this file under the MPL, indicate your decision by
 *   deleting  the provisions above and replace  them with the notice and
 *   other provisions required by the GPL License.  If you do not delete
 *   the provisions above, a recipient may use your version of this file
 *   under either the MPL or the GPL License."
 *
 *   [NOTE: The text of this Exhibit A may differ slightly from the text of
 *   the notices in the Source Code files of the Original Code. You should
 *   use the text of this Exhibit A rather than the text found in the
 *   Original Code Source Code for Your Modifications.]
 *
 * Created: Conrad S Roche <derupe at users.sourceforge.net>,  25-Jul-2000
 */

package com.conradroche.matra.dtdparser;

import java.util.*;

import com.conradroche.dtd.parser.CommentReader;
import com.conradroche.dtd.parser.NotationReader;
import com.conradroche.dtd.parser.PIReader;

import com.conradroche.matra.decl.AttList;
import com.conradroche.matra.decl.DocType;
import com.conradroche.matra.decl.ElementType;
import com.conradroche.matra.decl.Entity;

import com.conradroche.matra.data.DTDData;
import com.conradroche.matra.data.Data;

import com.conradroche.matra.exception.*;

import com.conradroche.matra.io.DTDSource;
import com.conradroche.matra.io.DTDUrl;

import com.conradroche.matra.parser.CommentReaderImpl;
import com.conradroche.matra.parser.NotationReaderImpl;
import com.conradroche.matra.parser.PIReaderImpl;

/**
 * Class to parse a DTD resource.
 *
 * @author: Conrad Roche
 */
public class DTDParser {

	private DTDData dtd;
	private DocType doctype = new DocType();
	private DTDSource dtdSrc;

	/**
	 * Constant to store the token identifying an
	 * element type declaration.
	 */
	public static final String ELEMENT_DECL = "ELEMENT";

	/**
	 * Constant to store the token identifying an
	 * attribute declaration.
	 */
	public static final String ATTRIBUTE_DECL = "ATTLIST";

	/**
	 * Constant to store the token identifying an
	 * entity declaration.
	 */
	public static final String ENTITY_DECL = "ENTITY";

	/**
	 * Constant to store the token identifying a
	 * notation declaration.
	 */
	public static final String NOTATION_DECL = "NOTATION";

	//Flags for various stuff
	private boolean parsed, builtTree;

	/**
	 * Create an instance of an CommentReader
	 * (use the appropriate implementing class)
	 * to read the comments.
	 */
	private CommentReader commentReader = new CommentReaderImpl();

	/**
	 * Create an instance of an NotationReader
	 * (use the appropriate implementing class)
	 * to read the Notation declaration.
	 */
	private NotationReader notationReader = new NotationReaderImpl();

	/**
	 * Create an instance of an PIReader
	 * (use the appropriate implementing class)
	 * to read the PI.
	 */
	private PIReader piReader = new PIReaderImpl();

/**
 * DTDParser constructor comment.
 */
public DTDParser() {
	super();

	dtd = new DTDData("");
	reset();
}
/**
 * Insert the method's description here.
 *
 * @param AttListDecl java.lang.String
 *
 * @throws DTDSyntaxException
 */
private void addAttributeList(String AttListDecl) throws DTDSyntaxException {

	if(AttListDecl == null || AttListDecl.equals(""))
		throw new IllegalArgumentException();

	AttList a = new AttList( AttListDecl );

	doctype.addAttList(a);
}
/**
 * Insert the method's description here.
 *
 * @param elemTypeDecl java.lang.String
 *
 * @throws DTDException
 */
private void addElementType(String elemTypeDecl) throws DTDException {

	if(elemTypeDecl == null || elemTypeDecl.equals(""))
		throw new IllegalArgumentException();

	ElementType e = new ElementType( elemTypeDecl );

	doctype.addElementType(e);
}
/**
 * Insert the method's description here.
 *
 * @param unresolvedDTD com.conradroche.matra.decl.Data
 *
 * @throws DTDException
 */
private void buildEntityList(DTDData unresolvedDTD) throws DTDException {

	if(unresolvedDTD == null)
		throw new IllegalArgumentException();

	unresolvedDTD.rewind();
	unresolvedDTD.skipWhiteSpace();

	while(!unresolvedDTD.endOfData()) {

		unresolvedDTD.skipWhiteSpace();

		if(unresolvedDTD.endOfData())
			return;

		if(commentReader.isCommentStart(unresolvedDTD)) {
			commentReader.readComment(unresolvedDTD);
			unresolvedDTD.skipWhiteSpace();
			continue;
		}

		if(notationReader.isNotationStart(unresolvedDTD)) {
			notationReader.readNotation(unresolvedDTD);
			unresolvedDTD.skipWhiteSpace();
			continue;
		}

		if(piReader.isPIStart(unresolvedDTD)) {
			piReader.readPI(unresolvedDTD);
			unresolvedDTD.skipWhiteSpace();
			continue;
		}

		char ch = unresolvedDTD.getNextChar();

		if(ch == '%') {
			String peEntityName = unresolvedDTD.getNextToken(';');

			Entity ent = doctype.getParamEntity(peEntityName);
			if(ent == null) {
				System.out.println("null entity - " + peEntityName);
			}
			if(ent.getExternalIDType() == Entity.EXT_ID_SYSTEM || ent.getExternalIDType() == Entity.EXT_ID_PUBLIC) {
				loadDTDEntities( ent.getSystemIdentifier() );
			}
			unresolvedDTD.skipWhiteSpace();
			continue;
		}
		if( ch != '<') {
			throw new DTDSyntaxException("Invalid character (" + unresolvedDTD.getPrevChar() + ") encountered at location " + unresolvedDTD.getCurrentLocation() + " - was expecting '<'.");
		}

		if( unresolvedDTD.endOfData() ) {
			throw new DTDSyntaxException("Unexpected end of data stream encountered at location " + unresolvedDTD.getCurrentLocation() + " - was expecting '!'.");
		}

		if( unresolvedDTD.getNextChar() != '!') {
			throw new DTDSyntaxException("Invalid character (" + unresolvedDTD.getPrevChar() + ") encountered at location " + unresolvedDTD.getCurrentLocation() + " - was expecting '!'.");
		}

		if( unresolvedDTD.endOfData() ) {
			throw new DTDSyntaxException("Unexpected end of data stream encountered after an '<!' at location " + unresolvedDTD.getCurrentLocation() + ".");
		}

		if( unresolvedDTD.lookNextChar() == '[' ) {
			DTDData condSect = unresolvedDTD.readConditionalSect();
			condSect.getNextChar(); //'['
			String sectType = condSect.getNextToken('[').trim();
			if(sectType.charAt(0) == '%') {
				String entName = sectType.substring(1, sectType.indexOf(";"));
				Entity ent = doctype.getParamEntity(entName);
				if(ent == null) {
					System.out.println("Got null entity for - '" + entName + "'.");
					System.exit(1);
				}
				sectType = ent.getLiteralValue();
			}
			//remove the ('[' 'INCLUDE' | 'IGNORE' & ']]>') before passing it to buildEntityList()

			//Ignore the ignored sections
			if(!sectType.equals("IGNORE")) {
				//CR: TODO: Check if this is correct
				if( (condSect.length() - condSect.getCurrentPosition()) < 3) {
					throw new DTDSyntaxException("Invalid conditional section encountered at location " + unresolvedDTD.getCurrentLocation() + ".");
				}

				buildEntityList( new DTDData(condSect.toString().substring(condSect.getCurrentPosition(), condSect.length() - 3)) );
				unresolvedDTD.skipWhiteSpace();
			}
			continue;
		}

		String name = unresolvedDTD.getNextToken();

		char nextChar;
		String content = "";

		//CR: TODO: is this getNextToken('>')?
		while( !unresolvedDTD.endOfData() && (nextChar = unresolvedDTD.lookNextChar()) != '>' )
			content += unresolvedDTD.getNextChar(); //trap error if eof reached before '>' char.

		if( unresolvedDTD.endOfData() ) {
			throw new DTDSyntaxException("Unexpected end of data stream encountered at location " + unresolvedDTD.getCurrentLocation() + " - was expecting '>'.");
		}

		//read the '>' char
		if(unresolvedDTD.getNextChar() != '>')
			throw new DTDSyntaxException("Invalid character (" + unresolvedDTD.lookNextChar() + ") encountered at location " + unresolvedDTD.getCurrentLocation() + " - was expecting '>'.");

		//resolve entity references - if possible
		content = resolveEntities(new DTDData(content)).toString();

		if( name.equals(ENTITY_DECL) ) {
			Entity ent = new Entity( content, doctype.getEntityList() );
			doctype.addEntity(ent);
		}
		unresolvedDTD.skipWhiteSpace();
	}

}
/**
 * Insert the method's description here.
 *
 * @throws DTDException
 */
private void buildTree() throws DTDException {

	Enumeration roots = doctype.getRootElements();

	if(roots != null) {
		while( roots.hasMoreElements() ) {
			ElementType root = (ElementType) doctype.getElementType( (String) roots.nextElement() );

			if(root == null) {
				throw new DTDException("Root element (" + (String) roots.nextElement() + ") definition not found in the DTD.");
			}
			root.load( doctype.getElementList(), doctype.getAllAttributes() );
		}
		builtTree = true;
	}


/*
	if(rootElement.equals("#PCDATA"))
		return;

	ElementType root = (ElementType) ElementList.get(rootElement);

	if(root == null) {
		throw new DTDException("Root element (" + rootElement + ") definition not found in the DTD.");
	}

	root.load( ElementList, AttributeList );
*/
}
/**
 * Returns the doctype for the parsed DTD.
 *
 * @return The doctype for the parsed DTD.
 *
 * @throws DTDException If the dtd is not completely parsed yet.
 */
public DocType getDocType() throws DTDException {

	if(!parsed)
		throw new DTDException("DocType cannot be obtained before parsing.");

	return doctype;
}
/**
 * Insert the method's description here.
 *
 * @throws DTDException
 */
private void loadConditionalSect() throws DTDException {

	DTDData conditionalSect = dtd.readConditionalSect();
	dtd.skipWhiteSpace();
	loadConditionalSect(conditionalSect);

}
/**
 * Insert the method's description here.
 *
 * @param conditionalSect  a DTDData object
 *
 * @throws DTDException
 */
private void loadConditionalSect(DTDData conditionalSect) throws DTDException {

/*
[61] conditionalSect ::= includeSect | ignoreSect
[62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'

[31] extSubsetDecl ::= ( markupdecl | conditionalSect | PEReference | S )*
[29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl
[69] PEReference ::= '%' Name ';'

[63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
[64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
[65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)

*/

	if(conditionalSect == null)
		throw new IllegalArgumentException();

	if( conditionalSect.getNextChar() != '[') //'['
		throw new DTDSyntaxException("Invalid character (" + conditionalSect.getPrevChar() + ") encountered at location " + conditionalSect.getCurrentLocation() + " - was expecting '['.");

	conditionalSect.skipWhiteSpace(); //S?
	String sectType = resolveEntities( new DTDData(conditionalSect.getNextToken()) ).toString();


	if( sectType.equals("INCLUDE") || sectType.equals("INCLUDE[")) {

		if(sectType.equals("INCLUDE")) {
			conditionalSect.skipWhiteSpace(); //S?
			if( conditionalSect.getNextChar() != '[' ) //'['
				throw new DTDSyntaxException("Invalid character (" + conditionalSect.getPrevChar() + ") encountered at location " + conditionalSect.getCurrentLocation() + " - was expecting '['.");
		}

		//extSubsetDecl
		while( !conditionalSect.endOfData() ) {
			conditionalSect.skipWhiteSpace();
//			char ch = conditionalSect.lookNextChar();
			if(conditionalSect.checkNextChar(']') == Data.FOUND_CHAR) {
				//CR: TODO: do syntax check here
//				conditionalSect.getNextChar(); //']'
				conditionalSect.getNextChar(); //']'
				conditionalSect.getNextChar(); //'>'
				conditionalSect.skipWhiteSpace();
				continue;
			}

			if(conditionalSect.checkNextChar('%') == Data.FOUND_CHAR) { //PEReference
//				conditionalSect.getNextChar(); //'%'
				String entName = conditionalSect.getNextToken(';');
				Entity ent = (Entity) doctype.getParamEntity(entName);
				if( ent.getExternalIDType() == Entity.EXT_ID_SYSTEM ||
					ent.getExternalIDType() == Entity.EXT_ID_PUBLIC) {
					loadDTD( ent.getSystemIdentifier() );
				}
				conditionalSect.skipWhiteSpace();
				continue;
			}

			if(commentReader.isCommentStart(conditionalSect)) {
				commentReader.readComment(conditionalSect);
				continue;
			}

			if(notationReader.isNotationStart(conditionalSect)) {
				notationReader.readNotation(conditionalSect);
				dtd.skipWhiteSpace();
				continue;
			}

			if(piReader.isPIStart(conditionalSect)) {
				piReader.readPI(conditionalSect);
				dtd.skipWhiteSpace();
				continue;
			}

			if( conditionalSect.getNextChar() != '<' )
				throw new DTDSyntaxException("Invalid character (" + conditionalSect.getPrevChar() + ") encountered at location " + conditionalSect.getCurrentLocation() + " - was expecting '<'.");
			if( conditionalSect.getNextChar() != '!' )
				throw new DTDSyntaxException("Invalid character (" + conditionalSect.getPrevChar() + ") encountered at location " + conditionalSect.getCurrentLocation() + " - was expecting '!'.");

			if( conditionalSect.lookNextChar() == '[' ) //conditionalSect
				loadConditionalSect(conditionalSect);
			else { //elementdecl | AttlistDecl
				String name = conditionalSect.getNextToken();

				char nextChar;
				String content = "";

				while( !conditionalSect.endOfData() && (nextChar = conditionalSect.lookNextChar()) != '>' )
					content += conditionalSect.getNextChar(); //trap error if eof reached before '>' char.

				if(conditionalSect.getNextChar() != '>') //'>'
					throw new DTDSyntaxException("Invalid character (" + conditionalSect.lookNextChar() + ") encountered at location " + conditionalSect.getCurrentLocation() + " - was expecting '>'.");

				//resolve any entity references
				content = resolveEntities(new DTDData(content)).toString();

				//create the appropriate object
				if( name.equals(ELEMENT_DECL) ) { //elementdecl
					ElementType e = new ElementType(content);
					//ElementList.put(e.getName(), e);
					doctype.addElementType(e);
				}
				else if( name.equals(ATTRIBUTE_DECL) ) { //AttlistDecl
					AttList a = new AttList(content);
					//AttributeList.put(a.getEleName(), a);
					doctype.addAttList(a);
				}
			}
		}
	}
	else if( sectType.equals("IGNORE") || sectType.equals("IGNORE[") ) {
		//ignore it!
		//The conditional section returned will not have the beginning '[' char
		//for the sectType.equals("IGNORE[") case - but it doesn't matter since we ignore it anyway
		conditionalSect.readConditionalSect();
	} else {
		throw new DTDSyntaxException("Got invalid conditional section with section type '" + sectType + "'");
	}
}
/**
 * Insert the method's description here.
 *
 * @param filename java.lang.String
 *
 * @throws DTDException
 */
private void loadDTD(String filename) throws DTDException {

	DTDParser parser = new DTDParser();
	//String dtdStr;

	//debug
//	System.out.println("Loading DTD ... " + filename);

	DTDSource newDtdSrc;

	//CR: TODO: Move this logic to determine the source type to a util file
	if(filename.startsWith("http")) {
		newDtdSrc = new DTDUrl(filename);
	} else {
		newDtdSrc = dtdSrc.getAbsolute(filename);
	}

	//if( filename.indexOf(':') != -1 || filename.charAt(0) == '\\' || filename.charAt(0) == '/' ) //if the new filename is an absolute path
		//dtdStr = parser.readDTDFile(filename);
	//else
		//dtdStr = parser.readDTDFile(getDTDDir() + '\\' + filename);

	parser.parse(newDtdSrc);

	Enumeration keys;

	//load the attribute list
	keys = parser.doctype.getAllAttributes().keys();
	while(keys.hasMoreElements()) {
		String key = (String) keys.nextElement();
		doctype.addAttList((AttList) parser.doctype.getAttributeList(key));
	}

	//load the element list
	keys = parser.doctype.getElementNames();
	while(keys.hasMoreElements()) {
		String key = (String) keys.nextElement();
		doctype.addElementType((ElementType) parser.doctype.getElementType(key));
	}

	//load the entity list
	keys = parser.doctype.getEntityList().keys();
	while(keys.hasMoreElements()) {
		String key = (String) keys.nextElement();
		doctype.addEntity((Entity) parser.doctype.getEntity(key));
	}

}
/**
 * Insert the method's description here.
 *
 * @param filename java.lang.String
 *
 * @throws DTDException
 */
private void loadDTDEntities(String filename) throws DTDException {

	if(filename == null || filename.equals(""))
		throw new IllegalArgumentException();

//	DTDParser parser = new DTDParser();
//	String dtdStr;
	DTDSource newDtdSrc;

	//CR: TODO: Move this logic to determine the source type to a util file
	if(filename.startsWith("http")) {
		newDtdSrc = new DTDUrl(filename);
	} else {
		newDtdSrc = dtdSrc.getAbsolute(filename);
	}

	buildEntityList(newDtdSrc.read());
//	if( filename.indexOf(':') != -1 || filename.charAt(0) == '\\' || filename.charAt(0) == '/' ) //if the new filename is an absolute path
//		dtdStr = parser.readDTDFile(filename);
//	else
//		dtdStr = parser.readDTDFile(getDTDDir() + '\\' + filename);

	//load the Entities in the current instance of the DTDParser.
//	buildEntityList(new DTDData(dtdStr));



	//load the entity list
	//keys = parser.EntityList.keys();
	//while(keys.hasMoreElements()) {
		//String key = (String) keys.nextElement();
		//EntityList.put(key, (Entity) parser.EntityList.get(key));
	//}
}
/**
 * Insert the method's description here.
 * @return com.conradroche.matra.decl.Data
 * @param unresolvedDTD com.conradroche.matra.decl.Data
 */
private void loadEntities(DTDData unresolvedDTD) throws DTDException {

	if(unresolvedDTD == null)
		throw new IllegalArgumentException();

	/*
	 * 4.6 Predefined Entities [XML Rec 1.0]
	 *
	 * <!ENTITY lt "&#38;#60;">
	 * <!ENTITY gt "&#62;">
	 * <!ENTITY amp "&#38;#38;">
	 * <!ENTITY apos "&#39;">
	 * <!ENTITY quot "&#34;">
	 */

	doctype.addEntity(new Entity("lt", "&#38;#60;", Entity.GLOBAL_ENTITY));

	doctype.addEntity(new Entity("gt", "&#62;", Entity.GLOBAL_ENTITY));

	doctype.addEntity(new Entity("amp", "&#38;#38;", Entity.GLOBAL_ENTITY));

	doctype.addEntity(new Entity("apos", "&#39;", Entity.GLOBAL_ENTITY));

	doctype.addEntity(new Entity("quot", "&#34;", Entity.GLOBAL_ENTITY));

	buildEntityList(unresolvedDTD);

}
/**
 * Parses the specified DTD.
 *
 * @param dtdSrc The location of the DTD.
 *
 * @throws DTDException If parsing of the DTD failed.
 */
public void parse(DTDSource dtdSrc) throws DTDException {

	//debug
//	System.out.println("begin parsing ... " + dtdSrc.getDTDLocation());

	if(dtdSrc == null)
		throw new IllegalArgumentException();

	reset();

	this.dtdSrc = dtdSrc;

	dtd = dtdSrc.read();

	loadEntities(dtd);
	dtd.rewind();

	dtd.skipWhiteSpace();

	while(!dtd.endOfData()) {
		parseNextSection();
	}

	parsed = true;

	//debug
//	System.out.println("end parsing ... " + dtdSrc.getDTDLocation());
//	System.out.println("Entity List - \n" + doctype.getEntityList());

	buildTree();
}
/**
 * Parses the specified DTD string.
 *
 * @param dtdString The DTD to be parsed.
 *
 * @throws DTDException If parsing of the DTD failed.
 */
public void parse(String dtdString) throws DTDException {

	if(dtdString == null || dtdString.equals(""))
		throw new IllegalArgumentException();

	reset();

	dtd = new DTDData(dtdString);

	loadEntities(dtd);
	dtd.rewind();

	dtd.skipWhiteSpace();

	while(!dtd.endOfData()) {
		parseNextSection();
	}

	parsed = true;

	buildTree();
}

/**
 * Insert the method's description here.
 *
 * @throws DTDException
 */
private void parseNextSection() throws DTDException {

	dtd.skipWhiteSpace();

	if(dtd.endOfData())
		return;

	if(commentReader.isCommentStart(dtd)) {
		commentReader.readComment(dtd);
		dtd.skipWhiteSpace();
		return;
	}

	if(notationReader.isNotationStart(dtd)) {
		//CR: TODO: store & process the Notation read.
		notationReader.readNotation(dtd);
		dtd.skipWhiteSpace();
		return;
	}

	if(piReader.isPIStart(dtd)) {
		piReader.readPI(dtd);
		dtd.skipWhiteSpace();
		return;
	}

	if(dtd.checkNextChar('%') == Data.FOUND_CHAR) { //PEReference
//		dtd.getNextChar(); //'%'
		String entName = dtd.getNextToken(';');
		Entity ent = doctype.getParamEntity(entName);
		if(ent.getExternalIDType() == Entity.EXT_ID_SYSTEM || ent.getExternalIDType() == Entity.EXT_ID_PUBLIC) {
			//System.out.println("Loading dtd - " + ent.getSystemIdentifier());
			loadDTD( ent.getSystemIdentifier() );
		}
		else { //pending
			//get the entity value and parse it in the current location.
		}
		dtd.skipWhiteSpace();
		return;
	}
	//what about General Entity references? ('&' GEntityName ';')

	if( dtd.getNextChar() != '<')
		throw new DTDSyntaxException("Invalid character (" + dtd.getPrevChar() + ") encountered at location " + dtd.getCurrentLocation() + " - was expecting '<'.");

	if( dtd.getNextChar() != '!')
		throw new DTDSyntaxException("Invalid character (" + dtd.getPrevChar() + ") encountered at location " + dtd.getCurrentLocation() + " - was expecting '!'.");

	if( dtd.lookNextChar() == '[' ) { //INCLUDE, IGNORE section?
		loadConditionalSect();
		dtd.skipWhiteSpace();
		return;
	}

	String sectName = dtd.getNextToken();

	String content = "";

	content = dtd.getNextToken('>');

	//check that the last char is '>'
	if(dtd.getPrevChar() != '>')
		throw new DTDSyntaxException("Invalid character (" + dtd.getPrevChar() + ") encountered at location " + dtd.getCurrentLocation() + " - was expecting '>'.");

	//resolve any entity references
	content = resolveEntities(new DTDData(content)).toString();

	//create the appropriate object
	if( sectName.equals(ELEMENT_DECL) ) {
		addElementType( content );
	}
	else if( sectName.equals(ATTRIBUTE_DECL) ) {
		addAttributeList( content );
	}
	else if( sectName.equals(ENTITY_DECL) ) {
		//already read in loadEntities - no entities should be encountered here.
	}
	else if( sectName.equals(NOTATION_DECL) ) {
		//ignore
	}
	else {
		throw new DTDSyntaxException("Invalid section encountered with name '" + sectName + "' at location " + dtd.getCurrentLocation() + " with content - \"" + content + "\".");
	}

	dtd.skipWhiteSpace();
}
/**
 * Insert the method's description here.
 */
private void reset() {

	dtd.reset();

	//dir = "";
	//file = "";

	doctype = new DocType();

	parsed = false;
	builtTree = false;
}
/**
 * Insert the method's description here.
 *
 * @param content com.conradroche.matra.decl.Data
 *
 * @return com.conradroche.matra.decl.Data
 */
private DTDData resolveEntities(DTDData content) {

	/*
	 * Entities will be in the form -
	 *   &entityName;  for Global Entities
	 *   %entityName; for Parameter Entities
	 *
	 *   &#n; for char reference (where n is a decimal number)
	 *   &#xn; for char reference (where n is a hex number)
	 */
	if(content == null)
		throw new IllegalArgumentException();

	String newContent = "";
	char[] entityDelim = { '&', '%' };

	newContent += content.getNextToken( entityDelim );

	while( !content.endOfData() ) {
		boolean pEntity = (content.getPrevChar() == '%' ? true : false);

		String entName = content.getNextToken(';');
		Entity entity = (Entity) doctype.getEntity( entName );

		if( entity == null ) {
			if(entName.charAt(0) == '#') { //CharRef
				int index = 10;

				String charValue;

				if(entName.charAt(1) == 'x') { //hex char
					index = 16;
					charValue = entName.substring(2, entName.length());
				} else {
					charValue = entName.substring(1, entName.length());
				}

				try {
					//CR: FIXME: Will this work for non-ascii chars
					char ch = (char) Integer.parseInt(charValue, index);
					newContent += ch;
				} catch(NumberFormatException ne) {
					//CR: TODO: Handle this
					System.out.println("Invalid value at location " + content.getCurrentPosition() +
								" char = " + charValue + ", index = " + index +
								" while parsing - \n" + content);
				}

			}
			else
				newContent += (pEntity ? '%' : '&') + entName + ';';
		}
		else {
			String entValue = entity.getResolvedValue(doctype.getEntityList());
			newContent += entValue;
		}
		newContent += content.getNextToken( entityDelim );
	}

	return new DTDData(newContent);
}
}